﻿using System;
using System.IO;
using System.Net;
using Noria.Web.MyMSDN.Workers;
using Noria.Web.Utility;
using MmContext = Noria.Web.MyMSDN.Models.MmContext;

namespace Noria.Web.MyMSDN.Tasks
{
    internal class SpiderTask : NTask
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        private static readonly log4net.ILog slog = log4net.LogManager.GetLogger("Noria.Spider");
        private static readonly log4net.ILog alog = log4net.LogManager.GetLogger("Noria.Audit");
        private bool _ran = false;

        protected override bool Enabled()
        {
            return (bool)NConfig.Root.spider.enabled;
        }

        protected override void Execute()
        {
            slog.Info("Begin spider execution.");
            try
            {
                // clear local database
                using (var db = new MmContext())
                {
                    db.Database.ExecuteSqlCommand("SET FOREIGN_KEY_CHECKS=0");
                    db.Database.ExecuteSqlCommand("truncate `checksum`");
                    db.Database.ExecuteSqlCommand("truncate `file`");
                    db.Database.ExecuteSqlCommand("truncate `family`");
                    db.Database.ExecuteSqlCommand("truncate `category`");
                    db.Database.ExecuteSqlCommand("SET FOREIGN_KEY_CHECKS=1");
                    alog.Info("mymsdn database has been truncated.");
                }

                // crawl data from microsoft.com
                using (IStatistics stat = new SimpleStatistics("msdn"))
                {
                    try
                    {
                        MsdnWorker.Create(stat, Waiter).Work();
                    }
                    catch
                    {
                        stat.Fault();
                        throw;
                    }
                }
                // retrieve checksum data from mymsdn.info
                using (IStatistics stat = new SimpleStatistics("mymsdn.get"))
                {
                    try
                    {
                        MyMSDNWorker.Create(stat, Waiter).Work();
                    }
                    catch
                    {
                        stat.Fault();
                        throw;
                    }
                }
                // crawl checksum data from itellyou.cn
                using (IStatistics stat = new SimpleStatistics("itellyou"))
                {
                    try
                    {
                        ChecksumWorker.Create(stat, Waiter).Work();
                    }
                    catch
                    {
                        stat.Fault();
                        throw;
                    }
                }

                if (!Waiter.IsAlive)
                    throw new ApplicationException("Spider task has been cancelled.");

                // merge checksum data to master data
                using (var db = new MmContext())
                {
                    int s = db.SyncChecksum();
                    alog.InfoFormat("Checksum synced to `file`, {0} updated.", s);
                }

                // transform data back to mymsdn.info
                using (IStatistics stat = new SimpleStatistics("mymsdn.set"))
                {
                    try
                    {
                        ((MyMSDNWorker) MyMSDNWorker.Create(stat, Waiter)).Upload();
                    }
                    catch
                    {
                        stat.Fault();
                        throw;
                    }
                }
            }
            catch (WebException e)
            {
                HandleWebException(e);
            }
            catch (Exception e)
            {
                slog.Error(e);
            }
            slog.Info("End spider execution.");
        }

        private void HandleWebException(WebException e)
        {
            try
            {
                using (var s = e.Response.GetResponseStream())
                {
                    var sr = new StreamReader(s);
                    var buffer = sr.ReadToEnd();
                    slog.Error(buffer, e);
                }
            }
            catch(Exception ex)
            {
                slog.Error("Failed to parse web exception", ex);
                slog.Error(e);
            }
        }

        protected override int GetNextOccurrenceSleeping()
        {
            if (NConfig.Root.spider.scheduler.test != null)
            {
                if (_ran)
                    return int.MaxValue;
                else
                    _ran = true;

                // delay 15 seconds for test purpose
                return (int)NConfig.Root.spider.scheduler.test * 1000;
            }
            else
            {
                int delay = NConfig.Root.spider.scheduler.delay;
                delay -= 1;

                // get UTC-8 current timestamp
                DateTime now = DateTime.UtcNow.AddHours(-8);
                log.DebugFormat("UTC-8 now: {0}", now);

                // calculate the next occurrence
                int sleeping = ((24 * 3600) - (now.Hour * 3600 + now.Minute * 60 + now.Second)) * 1000
                    + (delay * 24 * 3600 * 1000);

                return sleeping;
            }
        }
    }
}